/*
	Copyright (C) 2008 Demiurg-HG

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/


#include "..\global.h"

/*-----------------------------------------------------------------------------
	EVidProfiler
-----------------------------------------------------------------------------*/
#if 0
LPDIRECT3DQUERY9	vid_prof_query = NULL;

EVidProfiler::EVidProfiler(void)
{
	HRCALL( rs->GetD3DDevice()->CreateQuery(D3DQUERYTYPE_PIXELTIMINGS,	&vid_prof_query) );
	if (!vid_prof_query) {
		LOGF("WARNING : CreateQuery() failed\r\n");
	}
}


EVidProfiler::~EVidProfiler(void)
{
	SAFE_RELEASE(vid_prof_query);
}


void EVidProfiler::BeginEvent(const char *name)
{
}


void EVidProfiler::EndEvent(void)
{
}
#endif

/*-----------------------------------------------------------------------------
	Profiler :
-----------------------------------------------------------------------------*/

EProfiler::EProfiler(const char *name)
{
	is_reset = false;
	prof_name = name;
	ASSERT( QueryPerformanceCounter( (LARGE_INTEGER*)&start_time ) );
	ASSERT( QueryPerformanceFrequency( (LARGE_INTEGER*)&frequency ) );

	ResetProfiler();	
}


EProfiler::~EProfiler(void)
{
	ResetProfiler();
	for (uint i=0; i<events.size(); i++) {
		delete events[i];
	}
}


//
//	EProfiler::ResetStats
//
void EProfiler::ResetProfiler(void) 
{
	is_reset = true;
}


//
//	EProfiler::FindEvent
//
profEvent_s *EProfiler::FindEvent(const char *name)
{
	for (uint i=0; i<events.size(); i++) {
		if (strcmp(name, events[i]->name)==0) {
			return events[i];
		}
	}

	profEvent_s *event	= new profEvent_s;
	event->name			= name;
	event->total_time	= 0;
	event->enter_time	= 0;
	event->hit_count	= 0;
	
	events.push_back(event);
	return event;
}


//
//	EProfiler::MSec
//
float EProfiler::MSec(void)
{
	uint64_t time, dtime;
	ASSERT( QueryPerformanceCounter( (LARGE_INTEGER*)&time ) );
	dtime = time - start_time;
	
	double ftime  = dtime;
	ftime /= frequency;
	ftime *= 1000.0;
	
	return ftime;	
}


//
//	EProfiler::BeginEvent
//
void EProfiler::BeginEvent(const char *name)
{
	profEvent_s *event = FindEvent(name);
	event_stack.push(event);
	
	event->enter_time = MSec();
}


//
//	EProfiler::EndEvent
//
void EProfiler::EndEvent(void)
{
	profEvent_s *event = event_stack.top();

	double delta = MSec() - event->enter_time;
	event->total_time += delta;
	event->hit_count ++;
	
	event_stack.pop();
}


//
//	EProfiler::LoopCount
//
void EProfiler::Sync(void)
{
	if (is_reset) {
		is_reset = false;
		for (uint i=0; i<events.size(); i++) {
			delete events[i];
		}
		events.clear();

		old_time		=	MSec();
		total_loop_time	=	0;
		num_frames		=	0;
	}


	double time  = MSec();
	double dtime = time - old_time;
	old_time = time;
	total_loop_time += dtime;
	num_frames ++;
}

int ProfEventSortCallBack(const void *a, const void *b)
{
	profEvent_s	**pa = (profEvent_s**)a;
	profEvent_s	**pb = (profEvent_s**)b;
	
	if ((*pa)->total_time >  (*pb)->total_time) return -1;
	if ((*pa)->total_time <  (*pb)->total_time) return +1;
	if ((*pa)->total_time == (*pb)->total_time) return  0;
	return 0;
}

//
//	EProfiler::DumpStats
//
void EProfiler::DumpStats(IConsole *con, bool per_frame)
{
	LOG_SPLIT(va("Profiler : %s", prof_name.c_str()));
	
	/*LOGF("Frequency  : %I64d\r\n", frequency);
	LOGF("Start time : %I64d\r\n", start_time);*/

	if (total_loop_time==0 || num_frames==0) {
		LOGF("No loop counting information.\r\n");
		LOGF("Call EProfiler::CountFrameTime() at each frame.\r\n");
		goto _exit_dump_stats;
	}

	double	rcp_num_frames = 1.0 / (double)num_frames;

	if (per_frame) {
		LOGF("Average frame time for %d frames : %0.3f ms\r\n", num_frames, total_loop_time * rcp_num_frames);
	} else {
		LOGF("Total frame time for %d frames : %0.3f ms\r\n", num_frames, total_loop_time);
	}

	LOGF("Event Name                        %%   Hits           Total   Average\r\n");
	
	qsort(&events[0], events.size(), sizeof(profEvent_s*), ProfEventSortCallBack); 
	
	for (uint i=0; i<events.size(); i++) {
	
		if (events[i]->hit_count!=0) {
			uint	hits		=	events[i]->hit_count;
			double	total		=	events[i]->total_time;
			double	average		=	events[i]->total_time / hits;
			double	percentage	=	100 * total / total_loop_time;
			
			if (per_frame) {
				hits /= num_frames;
				total*= rcp_num_frames;
			}
		
			LOGF("%-30s %3.0f%% %6d %15.3f %9.3f\r\n", events[i]->name, percentage, hits, total, average );
			
			
		} else {
			LOGF("%-32s  - no timing info\r\n", events[i]->name);
		}
	}

_exit_dump_stats:;	
	LOG_SPLIT("");
}
